{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# Training TensorFlow (Keras) Deep Learning Models Efficiently\n",
    "\n",
    "In this notebook, we present an **efficient methodology** to train Deep Learning models for image classification.\n",
    "\n",
    "In practical Deep Learning projects, data is typically stored on disks. The **tf.Data** API provides faster solutions to load large data from the disk and apply various transformations on it. This API uses the **Dataset** object for representing a very large set of elements. It allows us to assert finer control over the data pipeline.\n",
    "\n",
    "Using the tf.Data API, we construct a Dataset object from the local image repository. Then, the Dataset object is transformed for loading image-label pairs. Prior loading, we convert each encoded image (i.e., PNG-encoded images) as a Tensor object, type-casting it (e.g., float32), scaling, getting the image label from the stored images (from the image sub-directories organized by class names). Finally, image-label pairs are shuffled and put into batches for training the model. \n",
    "\n",
    "This program uses the **distributed training** technique. It utilizes multiple GPUs on a single node. The specification for the number and type of GPUs should be provided in the SLURM .sh job request file. More information on distributed training: https://keras.io/guides/distributed_training/\n",
    "\n",
    "\n",
    "\n",
    "## Distributed Training: Single-host & Multi-device Synchronous Training\n",
    "\n",
    "We use the **tf.distribute** API to train a TensorFlow Keras model on multiple GPUs installed on a single machine (node). \n",
    "\n",
    "Specifically, we use the tf.distribute.Strategy with tf.keras. The tf.distribute.Strategy is integrated into tf.keras, which is a high-level API to build and train models. By integrating into tf.keras backend, it is seamless for use to distribute the training written in the Keras training framework.\n",
    "\n",
    "More information on the distributed training with Keras:\n",
    "https://www.tensorflow.org/tutorials/distribute/keras\n",
    "\n",
    "\n",
    "### Parallelism Technique:\n",
    "Via the tf.distribute API, we implement the synchronous **data parallelism** technique. In this technique, a single model gets replicated on multiple devices or multiple machines. Each of them processes different batches of data, then they merge their results. The different replicas of the model stay in sync after each batch they process. Synchronicity keeps the model convergence behavior identical to what you would see for single-device training.\n",
    "\n",
    "\n",
    "### How to use the tf.distribute API for distributed training:\n",
    "To perform single-host, multi-device synchronous training with a TensorFlow Keras model, we use the tf.distribute.MirroredStrategy API. \n",
    "\n",
    "Following are the 3 simple steps.\n",
    "\n",
    "- Instantiate a MirroredStrategy.\n",
    "By default, the strategy will use all GPUs available on a single machine.\n",
    "\n",
    "- Use the strategy object to open a scope, and within this scope, \n",
    "create all the Keras objects you need that contain variables. \n",
    "More specifically, within the distribution scope:\n",
    "- Create the model\n",
    "- Compile the model (by defining the optimizer and metrics)\n",
    "\n",
    "- Train the model via the fit() method as usual (outside the scope).\n",
    "\n",
    "**Note**: we need to use tf.data.Dataset objects to load data in a multi-device or distributed workflow.\n",
    "\n",
    "\n",
    "\n",
    "## Local Data Repository\n",
    "\n",
    "This program assumes that the training and test data (i.e., images) are stored locally, and organized in nested directories as follows:\n",
    "- train\n",
    "   - class_name_1\n",
    "   - class_name_2\n",
    "   ...\n",
    "- test\n",
    "   - class_name_1\n",
    "   - class_name_2\n",
    "   ...\n",
    "\n",
    "Specifically, there should be two root directories named \"train\" and \"test\". The sub-directories should be named after the classes.\n",
    "\n",
    "## Data Augmentation\n",
    "\n",
    "In the distributed training setting, we recommend performing data augmentation inside the model so that it is done by GPUs. We use Keras image preproessing layer API to build the data augmentation pipeline. https://www.tensorflow.org/api_docs/python/tf/keras/layers/experimental/preprocessing\n",
    "\n",
    "The augmentation layer can be used inside the data preprocessing pipeline when training is done by a single device (CPU or GPU).\n",
    "\n",
    "Thus, while the data preprocessing pipeline is built using tf.Data API, the data augmentation pipeline is based on Keras image preproessing layer API, which can be integrated in the preprocessing pipeline when need be.\n",
    "\n",
    "For a detail discussion on various augmentation approaches, see the following GitHub repository:\n",
    "https://github.com/rhasanbd/Data-Augmentation-for-Deep-Learning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # Use comet ml to track the progress of the training and get training statistics\n",
    "# from comet_ml import Experiment\n",
    "\n",
    "# # Create an experiment with the api key:\n",
    "# experiment = Experiment(\n",
    "#     api_key=\"\",\n",
    "#     project_name=\"\",\n",
    "#     workspace=\"\",\n",
    "#     log_code = True,\n",
    "#     auto_param_logging=True\n",
    "# )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Import statements\n",
    "import warnings\n",
    "import os\n",
    "import time\n",
    "import numpy as np\n",
    "import random\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from sklearn.metrics import confusion_matrix, accuracy_score, classification_report\n",
    "\n",
    "\n",
    "import torch # torch is used to get GPU information\n",
    "import tensorflow as tf\n",
    "\n",
    "# Python Imaging Library is used for opening image files\n",
    "import PIL\n",
    "import PIL.Image\n",
    "\n",
    "import pathlib # required to access the image folders"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "No GPU available, using the CPU\n"
     ]
    }
   ],
   "source": [
    "# Variable to store number of available GPUs\n",
    "num_of_gpu = 0\n",
    "\n",
    "# Determine the number of GPUs available\n",
    "if torch.cuda.is_available():    \n",
    "    # Tell torch to use the GPU    \n",
    "    device = torch.device(\"cuda\")\n",
    "    \n",
    "    # Get the number of GPUs\n",
    "    num_of_gpu = torch.cuda.device_count()\n",
    "    print(\"Number of available GPU(s) %d.\" % num_of_gpu)\n",
    "\n",
    "    print(\"GPU Name: \", torch.cuda.get_device_name(0))\n",
    "else:\n",
    "    print(\"No GPU available, using the CPU\")\n",
    "    device = torch.device(\"cpu\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.\n",
      "WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.\n",
      "INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0',)\n",
      "\n",
      "Number of GPU(s): 1\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "Create a MirroredStrategy\n",
    "The training model must be created and compiled inside the scope of this strategy\n",
    "'''\n",
    "strategy = tf.distribute.MirroredStrategy()\n",
    "print(\"\\nNumber of GPU(s): {}\".format(strategy.num_replicas_in_sync))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a Data Pipeline \n",
    "\n",
    "\n",
    "We create a data pipeline to perform the following tasks:\n",
    "- Load the data from the local storage into the program's memory\n",
    "- Prepare the dataset for training\n",
    "\n",
    "\n",
    "## Load Data from Storage \n",
    "\n",
    "We use TensorFlow's Data API tf.data to load and preprocess a large dataset efficiently. This API loads the data as tf Dataset object, which is required for distributed training (using multiple GPUs) and faster processing.\n",
    "\n",
    "\n",
    "## Approach for Loading the Images and their Labels\n",
    "\n",
    "- Create the list of file names (filepaths)\n",
    "- Create tf Dataset that consumes the list of file names \n",
    "- From this list, load the (image, label) pairs\n",
    "\n",
    "\n",
    "## Create the List of File Names (Filepaths)\n",
    "\n",
    "We get the list of file names (filepaths) by accessig the image folders. We use Path and glob methods in the pathlib module for accessing the image folders. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train directory path:  /Users/hasan/datasets/cifar10/train\n",
      "Test directory path:  /Users/hasan/datasets/cifar10/test\n",
      "\n",
      "Number of training files:  50000\n",
      "Number of test files:  10000\n",
      "\n",
      "Path of the first training image:  /Users/hasan/datasets/cifar10/train/cat/3975.png\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<PIL.PngImagePlugin.PngImageFile image mode=RGB size=32x32 at 0x1A6B4502E8>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Get the path to the root directory\n",
    "ROOT_DIR = os.path.abspath(os.curdir)\n",
    "\n",
    "# Create the path to the image directories\n",
    "train_dir = os.path.join(ROOT_DIR, 'datasets/cifar10/train')\n",
    "test_dir = os.path.join(ROOT_DIR, 'datasets/cifar10/test')\n",
    "\n",
    "print(\"Train directory path: \", train_dir)\n",
    "print(\"Test directory path: \", test_dir)\n",
    "\n",
    "# Create a list of training filenames\n",
    "train_data_dir = pathlib.Path(train_dir)\n",
    "train_file_paths = list(train_data_dir.glob('*/*')) # Get the list of all training image filepaths\n",
    "print(\"\\nNumber of training files: \", len(train_file_paths))\n",
    "\n",
    "# Create a list of test filenames\n",
    "test_data_dir = pathlib.Path(test_dir)\n",
    "test_file_paths = list(test_data_dir.glob('*/*')) # Get the list of all test image filepaths\n",
    "print(\"Number of test files: \", len(test_file_paths))\n",
    "\n",
    "print(\"\\nPath of the first training image: \", train_file_paths[0])\n",
    "PIL.Image.open(train_file_paths[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a tf Dataset that Consumes the List of File Names (Filepaths)\n",
    "\n",
    "There are two options to create the image file list Dataset.\n",
    "\n",
    "- Option 1: Create the file list Dataset by using the Dataset.from_tensor_slices() method\n",
    "- Option 2: Create the file list Dataset by using the Dataset.list_files() method\n",
    "\n",
    "Note that, before using Option 2, if the filenames have already been globbed, then re-globbing every filename with the list_files() method may result in poor performance with remote storage systems.\n",
    "\n",
    "Since, we have already globbed the file names & paths in the first step, we do not use Option 2.\n",
    "However, the code for option 2 is provided (commented out)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train filenames list length:  50000\n",
      "Test filenames list length:  10000\n",
      "\n",
      "Number of samples (image files) in training dataset:  50000\n",
      "Number of samples (image files) in test dataset:  10000\n"
     ]
    }
   ],
   "source": [
    "#_______________________Option 1: \"from_tensor_slices\" method____________________________\n",
    "\n",
    "\n",
    "'''\n",
    "Get filenames (filepaths) as strings in a list (for training & testing images) \n",
    "'''\n",
    "train_fnames=[]\n",
    "for fname in train_file_paths:\n",
    "    train_fnames.append(str(fname))\n",
    "\n",
    "print(\"Train filenames list length: \", len(train_fnames))\n",
    "\n",
    "test_fnames=[]\n",
    "for fname in test_file_paths:\n",
    "    test_fnames.append(str(fname))\n",
    "\n",
    "print(\"Test filenames list length: \", len(test_fnames))\n",
    "\n",
    "\n",
    "'''\n",
    "Shuffle the filepaths\n",
    "'''\n",
    "random.shuffle(train_fnames)\n",
    "random.shuffle(test_fnames)\n",
    "\n",
    "\n",
    "'''\n",
    "Create the filepath list Dataset (for training & testing images) \n",
    "'''\n",
    "train_dataset = tf.data.Dataset.from_tensor_slices(train_fnames)\n",
    "test_dataset = tf.data.Dataset.from_tensor_slices(test_fnames)\n",
    "\n",
    "\n",
    "\n",
    "#_______________________Option 2: \"list_flies\" method (NOT USED)__________________________\n",
    "\n",
    "# #Get a training dataset of all files (list of all image paths) matching the glob pattern\n",
    "# train_dataset = tf.data.Dataset.list_files(str(train_data_dir/'*/*'))\n",
    "# \n",
    "# #Get a test dataset of all files (list of all image paths) matching the glob pattern\n",
    "# test_dataset = tf.data.Dataset.list_files(str(test_data_dir/'*/*'))\n",
    "\n",
    "\n",
    "train_dataset_size = train_dataset.cardinality().numpy()\n",
    "test_dataset_size = test_dataset.cardinality().numpy()\n",
    "\n",
    "\n",
    "print(\"\\nNumber of samples (image files) in training dataset: \", train_dataset_size)\n",
    "print(\"Number of samples (image files) in test dataset: \", test_dataset_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Display Some Filepaths from the Dataset\n",
    "\n",
    "We display the file names (filepaths) of five files from the Dataset. Observe that\n",
    "- The file names are shuffled\n",
    "- The letter \"b\" appears before the filepaths because TensorFlow parses the strings as byte-strings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'/Users/hasan/datasets/cifar10/train/deer/1983.png'\n",
      "b'/Users/hasan/datasets/cifar10/train/dog/4592.png'\n",
      "b'/Users/hasan/datasets/cifar10/train/ship/1339.png'\n",
      "b'/Users/hasan/datasets/cifar10/train/horse/3619.png'\n",
      "b'/Users/hasan/datasets/cifar10/train/horse/1058.png'\n"
     ]
    }
   ],
   "source": [
    "for f in train_dataset.take(5):\n",
    "    print(f.numpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function for Loading the (image, label) Pair from the Filepath\n",
    "\n",
    "\n",
    "We define the \"load_labeled_data\" function that takes a filepath and returns the image-label pair. The image is loaded as a float32 Tensor (scaled) and the label is loaded as one-hot encoded int32 Tensor. This function is used by the Dataset to load image-label pairs from the list of filepaths.\n",
    "\n",
    "The \"load_labeled_data\" function is defined based on a few other functions below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Class names:\n",
      " ['cat', 'dog', 'truck', 'bird', 'airplane', 'ship', 'frog', 'horse', 'deer', 'automobile']\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "Get class names as a list\n",
    "The class names will be used by the \"get_label\" function\n",
    "NOTE: it only works if the data is structured in nested sub-directories named after the classes.\n",
    "'''\n",
    "CLASS_NAMES = os.listdir(train_data_dir)\n",
    "print(\"\\nClass names:\\n\", CLASS_NAMES)\n",
    "\n",
    "\n",
    "'''\n",
    "Function to get the label of an image (file) from its path and class names\n",
    "The labels are one-hot encoded\n",
    "'''\n",
    "def get_label(file_path):\n",
    "  # Convert the path to a list of path components\n",
    "  parts = tf.strings.split(file_path, os.path.sep)\n",
    "  # The second to last is the class-directory (i.e., name of the class)\n",
    "  return tf.cast(parts[-2] == CLASS_NAMES, dtype=tf.int32)\n",
    "  \n",
    "\n",
    "'''\n",
    "This function reads a PNG-encoded image into a uint8 tensor\n",
    "- Converts the uint8 tensor into float32\n",
    "- Scales the tensor into the range [0,1] automatically while converting to float32\n",
    "  Thus, we don't need to scale the images separately\n",
    "- Resizes the image\n",
    "Note: if the image encoding is other than PNG, then a suitable decode method should be used.\n",
    "'''   \n",
    "def process_image(image, image_height=32, image_width=32):  \n",
    "    # Decode a PNG-encoded image to a uint8 tensor\n",
    "    image = tf.image.decode_png(image, channels=3)\n",
    "\n",
    "    # Convert the unit8 tensor to floats in the [0,1] range\n",
    "    # Images that are represented using floating point values are expected to have values in the range [0,1]\n",
    "    image = tf.image.convert_image_dtype(image, tf.float32)\n",
    "    \n",
    "    # Resize image\n",
    "    image = tf.image.resize(image, [image_height, image_width])\n",
    "    \n",
    "    return image\n",
    "    \n",
    "'''\n",
    "This functions returns a one-hot encoded labeled float32 tensor of an image \n",
    "based on its file path\n",
    "'''\n",
    "def load_labeled_data(file_path):\n",
    "    img = tf.io.read_file(file_path)\n",
    "    img = process_image(img)\n",
    "    label = get_label(file_path)\n",
    "    return img, label "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function to Prepare the Dataset for Training\n",
    "\n",
    "To prepare the Dataset for training, we need to:\n",
    "- Get the (image, label) pairs from the list Dataset object (that contains list of file paths)\n",
    "- Load these pairs in memory\n",
    "- Create mini-batches for training and validation\n",
    "\n",
    "The Dataset object provides convenient methods for these and additional useful data preprocessing tasks. We chain the relevant methods on the Dataset object to:\n",
    "- Load (image, label) pairs into the Dataset object\n",
    "- Cache Dataset\n",
    "- Shuffle Dataset\n",
    "- Create mini-batches\n",
    "- Perform data augmentation\n",
    "- Prefetch a mini-batch\n",
    "\n",
    "First, we call the Dataset's map() method that applies the data loading function \"load_labeled_data\" on each sample of the dataset. The returned new object will emit transformed images and their one-hot encoded labels in the original order.\n",
    "\n",
    "Second, we cache the data locally. The \"cache\" method stores dataset elements in memory (by default) or in file for future reusing.\n",
    "- The first time the dataset is iterated over (i.e., first epoch), its elements will be cached either in the specified file or in memory. \n",
    "- Subsequent iterations (i.e., epochs) will use the cached data. This will save some operations (e.g., file opening, data reading, parsing, transforming, etc.) from being executed during each epoch.\n",
    "\n",
    "Caching should be used judiciously.\n",
    "- Smaller dataset (that fits into memory): use the cache method. \n",
    "- Large dataset:  typically is sharded (split in multiple files), and do not fit in memory. Thus, it should not be cached in memory.\n",
    "\n",
    "The cache method will produce exactly the same elements during each iteration (epoch) through the dataset. \n",
    "For randomizing the iteration order, we need to call the shuffle method after calling cache.\n",
    "\n",
    "Third, we randomly shuffle the dataset using the shuffle method. The shuffle method randomly shuffles the elements of the dataset. First, it fills a buffer with the dataset with buffer_size elements. Then, it randomly samples elements from this buffer, replacing the selected elements with new elements. For perfect shuffling, the buffer_size should be greater than or equal to the size of the dataset.  However, for large datasets, this isn't possible. So, we will use a large enough buffer_size.\n",
    "\n",
    "Fourth, we batch the dataset. In the batch method, we set \"drop_remainder\" to True so that the size of the training set is divisible by the batch_size. It is done by removing enough training examples. \n",
    "\n",
    "Finally, we prefetch a batch to decouple the time when data is produced from the time when data is consumed. The transformation uses a background thread and an internal buffer to prefetch elements from the input dataset ahead of the time they are requested. The number of elements to prefetch should be equal to (or possibly greater than)  the number of batches consumed by a single training step. Instead of manually tuning this value, we set it to tf.data.AUTOTUNE, which will prompt the tf.data runtime to tune the value dynamically at runtime."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def prepare_dataset(ds, mini_batch, epochs, shuffle=False, buffer_size=0, augment=False, augment_func=None):\n",
    "\n",
    "    '''\n",
    "    The map method applies the argument function (i.e., load_labeled_data) \n",
    "    to each element of the dataset, \n",
    "    and returns a new dataset containing the transformed elements (i.e., image-label pairs), \n",
    "    in the same order as they appeared in the input.\n",
    "    '''\n",
    "    ds = ds.map(load_labeled_data, num_parallel_calls=tf.data.AUTOTUNE)\n",
    "    \n",
    "    '''\n",
    "    Cache the dataset elements in memory\n",
    "    '''\n",
    "    ds = ds.cache()\n",
    "  \n",
    "    '''\n",
    "    Shuffle the dataset\n",
    "    '''\n",
    "    if shuffle:\n",
    "        ds = ds.shuffle(buffer_size)\n",
    "      \n",
    "    '''\n",
    "    Repeat the shuffled dataset\n",
    "    '''  \n",
    "    ds = ds.repeat(count=epochs)\n",
    "    \n",
    "\n",
    "    '''\n",
    "    Batch all datasets\n",
    "    '''\n",
    "    ds = ds.batch(mini_batch, drop_remainder=True)\n",
    "\n",
    "    '''\n",
    "    Use data augmentation only on the training set\n",
    "    When training is done using GPUs in a distributed setting, \n",
    "    for efficiency, perform data augmentation inside the model. \n",
    "    '''\n",
    "    if augment:\n",
    "        ds = ds.map(lambda x, y: (augment_func(x, training=True), y), num_parallel_calls=tf.data.AUTOTUNE)\n",
    "    \n",
    "    '''\n",
    "    Repeats the batch so each original value is seen \"count\" times\n",
    "    Increasing \"count\" will increase the number of steps per epoch\n",
    "    '''\n",
    "    #ds = ds.repeat(count=2) \n",
    "    \n",
    "    '''\n",
    "    Use buffered prefecting on all datasets\n",
    "    '''\n",
    "    return ds.prefetch(buffer_size=tf.data.AUTOTUNE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prepare Dataset for Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Size of training mini-batch:  64\n",
      "Size of test mini-batch:  64\n",
      "\n",
      "Buffer Size:  50000\n",
      "\n",
      "Epochs:  5\n",
      "Data available to run for 5 epochs\n",
      "Unlimited data available:  False\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "Determine the size of mini-batch for training\n",
    "It should be a multiple of BATCH_SIZE_PER_REPLICA \n",
    "The multiplication factor is determined by the number of available GPUs (or strategy.num_replicas_in_sync)\n",
    "'''\n",
    "                                    \n",
    "BATCH_SIZE_PER_REPLICA = 64\n",
    "\n",
    "if(num_of_gpu > 0):\n",
    "    size_of_mini_batch = BATCH_SIZE_PER_REPLICA*num_of_gpu\n",
    "else:\n",
    "    size_of_mini_batch = BATCH_SIZE_PER_REPLICA # Uses the CPU, as no GPU is available\n",
    "\n",
    "\n",
    "'''\n",
    "Size of test mini-batch \n",
    "'''\n",
    "size_of_mini_batch_test = size_of_mini_batch\n",
    "\n",
    "print(\"\\nSize of training mini-batch: \", size_of_mini_batch)\n",
    "print(\"Size of test mini-batch: \", size_of_mini_batch_test)\n",
    "\n",
    "\n",
    "'''\n",
    "Used by the \"shuffle\" method. \n",
    "For small dataset, it should be equal or larger than training set.\n",
    "'''\n",
    "buffer_size =   train_dataset_size\n",
    "print(\"\\nBuffer Size: \", buffer_size)\n",
    "\n",
    "\n",
    "'''\n",
    "Set the number of training epochs.\n",
    "This is required by the repeat method.\n",
    "'''\n",
    "no_of_epochs = 5\n",
    "print(\"\\nEpochs: \", no_of_epochs)\n",
    "\n",
    "\n",
    "'''\n",
    "Perform data preprocessing by the CPU.\n",
    "It is efficient as it ensures that the GPUs will be used only for model training.\n",
    "'''\n",
    "with tf.device('/cpu:0'):\n",
    "    train_loader = prepare_dataset(train_dataset, size_of_mini_batch, no_of_epochs, \n",
    "                                   shuffle=True, buffer_size=buffer_size, augment=False, augment_func=None)\n",
    "    test_loader = prepare_dataset(test_dataset, size_of_mini_batch_test, no_of_epochs)\n",
    "    no_of_steps_per_epoch = train_dataset_size//size_of_mini_batch\n",
    "    total_no_of_steps = train_loader.cardinality().numpy()\n",
    "    print(\"Data available to run for %d epochs\" % (total_no_of_steps//no_of_steps_per_epoch))\n",
    "    print(\"Unlimited data available: \", (train_loader.cardinality() == tf.data.INFINITE_CARDINALITY).numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 360x360 with 9 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "'''\n",
    "Display some training images from a training Dataset batch\n",
    "'''\n",
    "image_batch, label_batch = next(iter(train_loader))\n",
    "\n",
    "plt.figure(figsize=(5, 5))\n",
    "for i in range(9):\n",
    "    ax = plt.subplot(3, 3, i + 1)\n",
    "    plt.imshow(image_batch[i])\n",
    "    label = label_batch[i]\n",
    "    plt.title(CLASS_NAMES[tf.argmax(label)])\n",
    "    plt.axis(\"off\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Augmentation Layer\n",
    "\n",
    "We define a custom Data Augmentation layer by extending the Keras API. It uses the Keras Image Preprocessing Layers.\n",
    "\n",
    "\n",
    "All custom layers subclass the Layer class and implement:\n",
    "- call method: It specifies the computation done by the layer.\n",
    "- build method: It creates the weights of the layer (we can create weights in __init__, as well).\n",
    "- get_config method: If we need the custom layer to be serializable (e.g., when included in the learning model), we should implement the get_config method. This method is used to return the constructor arguments of the layer instance.\n",
    "\n",
    "\n",
    "When training is done by a single device (CPU or GPU), data augmentation should be done during preprocessing. However, in distributed training (using multiple GPUs), we can expedite the training time by integrating data augmentation into the learning model so that it is done by GPUs.\n",
    "\n",
    "In this demo, we perform data augmentation inside the model. For this, we ceate a data augmentation layer by defining a function named \"data_augmentation_layer\". The function is based on a data augmentation Sequential layer object (defined in the class \"DataAugmentation\"). This custom layer object is included in the learning model.\n",
    "\n",
    "\n",
    "To learn more about various techniques for data augmentation, see:\n",
    "https://github.com/rhasanbd/Data-Augmentation-for-Deep-Learning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "Define the DataAugmentation class using the tf.Keras' Sequential API.\n",
    "It creates a data augmentation layer for performing following augmentation to input data:\n",
    "- Resize (increase the size)\n",
    "- Random zoom\n",
    "- Random rotation\n",
    "- Random horizontal flip\n",
    "- Random crop (restore the original size)\n",
    "'''\n",
    "\n",
    "class DataAugmentation(tf.keras.layers.Layer):\n",
    "    def __init__(self, original_size, increased_size, rotation_angle_degree, zoom_height_factor, **kwargs):\n",
    "        super().__init__(**kwargs)\n",
    "        self.augmentation_layers = [] \n",
    "        self.augmentation_layers.append(tf.keras.layers.experimental.preprocessing.Resizing(increased_size, increased_size))\n",
    "        self.augmentation_layers.append(tf.keras.layers.experimental.preprocessing.RandomZoom(zoom_height_factor, width_factor=None, \n",
    "                                                                                              fill_mode='nearest', interpolation='bilinear'))\n",
    "        self.augmentation_layers.append(tf.keras.layers.experimental.preprocessing.RandomRotation(rotation_angle_degree/360, \n",
    "                                                                                                  fill_mode='nearest', interpolation='bilinear'))\n",
    "        self.augmentation_layers.append(tf.keras.layers.experimental.preprocessing.RandomFlip(\"horizontal\"))\n",
    "        self.augmentation_layers.append(tf.keras.layers.experimental.preprocessing.RandomCrop(original_size, original_size))\n",
    "        \n",
    "      \n",
    "    def call(self, inputs):\n",
    "        Z = inputs\n",
    "        for layer in self.augmentation_layers:\n",
    "            Z = layer(Z)\n",
    "        return Z\n",
    "    \n",
    "    # Required for the custom object's serialization\n",
    "    def get_config(self):\n",
    "        config = super().get_config().copy()\n",
    "        config.update({\"augmentation\": self.augmentation_layers})\n",
    "        return config\n",
    "  \n",
    "\n",
    "'''\n",
    "This function creates a sequential tf.Keras layer for performing data augmentation\n",
    "'''\n",
    "def data_augmentation_layer(original_size, increased_size, rotation_angle_degree, zoom_height_factor):\n",
    "    \n",
    "    d_augmentation = tf.keras.models.Sequential(name='Data-Augmentation')\n",
    "    d_augmentation.add(DataAugmentation(original_size, increased_size, rotation_angle_degree, zoom_height_factor))\n",
    "\n",
    "    return d_augmentation  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function for Creating the Training Model\n",
    "\n",
    "We define a function to create the VGG model. First, we define a custom VGG_Block class, which is used to create a VGGNet by the \"create_vgg_net\" function. The function also uses the custom data augmentation layer.\n",
    "\n",
    "For more information on the VGGNet, see:\n",
    "https://github.com/rhasanbd/Convolutional-Neural-Networks/blob/main/CNN-9-Notable%20Architectures-I.ipynb"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## VGG Block Class\n",
    "\n",
    "For serializability of this custom class, we override its get_config() method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "Define the VGG block class using Keras Sequential API \n",
    "The VGG_Block class takes two arguments:\n",
    "- conv_block_number: number of convolutional layers \n",
    "- num_of_channels: number of output channels \n",
    "'''\n",
    "class VGG_Block(tf.keras.layers.Layer):\n",
    "    def __init__(self, conv_block_number, num_of_channels, weight_decay, **kwargs):\n",
    "        super().__init__(**kwargs)\n",
    "        self.conv_layers = [] \n",
    "        for _ in range(conv_block_number):\n",
    "            self.conv_layers.append(tf.keras.layers.Conv2D(filters=num_of_channels, kernel_size=(3, 3), strides=1,\n",
    "                                padding='same', kernel_regularizer=tf.keras.regularizers.l2(weight_decay), use_bias=False))\n",
    "            self.conv_layers.append(tf.keras.layers.BatchNormalization())\n",
    "            self.conv_layers.append(tf.keras.layers.Activation(\"relu\"))\n",
    "        \n",
    "        self.pool_layer = tf.keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2, padding='valid')\n",
    "\n",
    "    def call(self, inputs):\n",
    "        Z = inputs\n",
    "        for layer in self.conv_layers:\n",
    "            Z = layer(Z)\n",
    "            \n",
    "        Z = self.pool_layer(Z)\n",
    "        return Z\n",
    "        \n",
    "    # Required for the custom object's serialization\n",
    "    def get_config(self):\n",
    "        config = super().get_config().copy()\n",
    "        config.update({\n",
    "            \"conv_layers\": self.conv_layers,\n",
    "            \"pool_layer\": self.pool_layer,\n",
    "        })\n",
    "        return config"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function for Creating a VGGNet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_vgg_net(conv_blocks, width, height, channels, num_classes, weight_decay, augmentation=False, **kwargs):\n",
    "    \n",
    "    vgg_net = tf.keras.models.Sequential(name='VGG')\n",
    "    \n",
    "    vgg_net.add(tf.keras.layers.InputLayer(input_shape=(width, height, channels)))\n",
    "    \n",
    "    # Data augmentation layer\n",
    "    if(augmentation):\n",
    "        vgg_net.add(data_augmentation_layer(**kwargs))\n",
    "    \n",
    "    # Convolutional layers based on the VGG_Block object\n",
    "    for (conv_block_number, num_of_channels) in conv_blocks:\n",
    "            vgg_net.add(VGG_Block(conv_block_number, num_of_channels, weight_decay))\n",
    "    \n",
    "    # Flatten the convnet output to feed it with fully-connected layers\n",
    "    vgg_net.add(tf.keras.layers.Flatten())\n",
    "    \n",
    "    # Fully-connected layers\n",
    "    vgg_net.add(tf.keras.layers.Dense(units=64, activation='relu'))\n",
    "    vgg_net.add(tf.keras.layers.Dropout(0.5))\n",
    "    vgg_net.add(tf.keras.layers.Dense(units=num_classes, activation='softmax'))\n",
    "    \n",
    "    return vgg_net"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"VGG\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "vgg__block (VGG_Block)       (None, 16, 16, 32)        10336     \n",
      "_________________________________________________________________\n",
      "vgg__block_1 (VGG_Block)     (None, 8, 8, 64)          55808     \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 4096)              0         \n",
      "_________________________________________________________________\n",
      "dense (Dense)                (None, 64)                262208    \n",
      "_________________________________________________________________\n",
      "dropout (Dropout)            (None, 64)                0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 10)                650       \n",
      "=================================================================\n",
      "Total params: 329,002\n",
      "Trainable params: 328,618\n",
      "Non-trainable params: 384\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "Delete the TensorFlow graph before creating a new model, otherwise memory overflow will occur.\n",
    "'''\n",
    "tf.keras.backend.clear_session()\n",
    "\n",
    "'''\n",
    "To reproduce the same result by the model in each iteration, we use fixed seeds for random number generation. \n",
    "'''\n",
    "np.random.seed(42)\n",
    "tf.random.set_seed(42)\n",
    "\n",
    "\n",
    "'''\n",
    "Define the optimizer\n",
    "'''\n",
    "optimizer = tf.keras.optimizers.SGD(learning_rate=1e-1, momentum=0.9, nesterov=False)\n",
    "\n",
    "\n",
    "'''\n",
    "We use the layer_info variable for creating the VGG model.\n",
    "It contains a list of tuples (one per block).\n",
    "Each tuple contains two values: \n",
    "- number of convolutional layers\n",
    "- the number of output channels\n",
    "These two arguments are used to call the VGG_Block function\n",
    "'''\n",
    "layer_info = ((2, 32), (2, 64)) \n",
    "\n",
    "\n",
    "'''\n",
    "Open a strategy scope to create and compile the model\n",
    "Everything that creates variables should be under the strategy scope\n",
    "'''\n",
    "with strategy.scope():\n",
    "    \n",
    "    '''\n",
    "    If the \"augment\" is set to True, then pass the following keyword arguments.\n",
    "    - original_size\n",
    "    - increased_size\n",
    "    - rotation_angle_degree\n",
    "    - zoom_height_factor\n",
    "    '''\n",
    "    model = create_vgg_net(layer_info, 32, 32, channels=3, num_classes=10,\n",
    "                           weight_decay=0.001,\n",
    "                           augment=True, \n",
    "                           original_size=32, \n",
    "                           increased_size=36, \n",
    "                           rotation_angle_degree=20, \n",
    "                           zoom_height_factor=0.6)\n",
    "    model.summary()\n",
    "    loss_object = tf.keras.losses.CategoricalCrossentropy(from_logits=True)\n",
    "    model.compile(loss=loss_object,\n",
    "              optimizer=optimizer,\n",
    "              metrics=[tf.keras.metrics.CategoricalAccuracy()])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot the Model as a Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tf.keras.utils.plot_model(model, \"VGGNet.png\", show_shapes=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Training\n",
    "\n",
    "\n",
    "We use the model.fit() method to train the model for a fixed number of epochs (iterations on a dataset).\n",
    "\n",
    "Since we repeat the dataset (train and validation) multiple times (equal to the number of epochs), we set the \"steps_per_epoch\" and \"validation_steps\" arguments of the fit method.\n",
    "\n",
    "- steps_per_epoch\n",
    "It specifies the total number of steps (batches of samples) before declaring one epoch finished and starting the next epoch. When training with input tf.data dataset, and 'steps_per_epoch' is None, the epoch will run until the input dataset is exhausted. When passing an infinitely repeating dataset, we must specify the steps_per_epoch argument.\n",
    "\n",
    "- validation_steps\n",
    "It specifies the total number of steps (batches of samples) to draw before stopping when performing validation at the end of every epoch. It is relevant only if validation_data is provided and is a tf.data dataset. If 'validation_steps' is None, validation will run until the validation_data dataset is exhausted. In the case of an infinitely repeated dataset, it will run into an infinite loop.\n",
    "\n",
    "\n",
    "### Model Serialization\n",
    "\n",
    "We save the best model using a callback function tf.keras.callbacks.ModelCheckpoint(). It is used in conjunction with training using model.fit() to save a model or weights (in a checkpoint file) at some interval, so the model or weights can be loaded later to continue the training from the state saved.\n",
    "\n",
    "Following two arguments need to be set carefully:\n",
    "\n",
    "- save_best_only\n",
    "We set its value to \"True\" so that the ModelCheckpoint() only saves when the model is considered the \"best\" and the latest best model according to the quantity monitored will not be overwritten.\n",
    "\n",
    "\n",
    "- save_weights_only\t\n",
    "We set its value to \"False\" so that the full model is saved (model.save(filepath)), instead of saving only the model's weights.\n",
    "\n",
    "\n",
    "### Note on the model.save() Method\n",
    "\n",
    "The deafult format for serialization used by the model.save() Method is the TensorFlow **SavedModel** format. Its alternative is the older Keras H5 format. We use the SavedModel format (so the model name does not have the .h5 extension).\n",
    "\n",
    "SavedModel is a comprehensive save format that saves: \n",
    "- the model architecture \n",
    "- weights\n",
    "- the traced Tensorflow subgraphs of the call functions \n",
    "\n",
    "This enables Keras to restore both built-in layers as well as custom objects (e.g., data augmentation layer).\n",
    "\n",
    "\n",
    "Calling model.save(model_name) creates a folder named model_name. It contains the following directories and file (.pb):\n",
    "- assets  \n",
    "- saved_model.pb  \n",
    "- variables\n",
    "\n",
    "The saved_model.pb file stores the actual TensorFlow program, or model. It includes the model architecture and training configuration (e.g., optimizer, losses, and metrics). \n",
    "\n",
    "The variables directory contains weights (i.e., a standard training checkpoint). For our model, it will save the weights of the best model so far.\n",
    "\n",
    "The assets directory contains files used by the TensorFlow graph. For example text files used to initialize vocabulary tables. For our model, this directory will be empty.\n",
    "\n",
    "For more informattion, see:\n",
    "https://keras.io/guides/serialization_and_saving/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Training started ...\n",
      "Epoch 1/5\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/hasan/anaconda3/lib/python3.7/site-packages/tensorflow/python/keras/backend.py:4870: UserWarning: \"`categorical_crossentropy` received `from_logits=True`, but the `output` argument was produced by a sigmoid or softmax activation and thus does not represent logits. Was this intended?\"\n",
      "  '\"`categorical_crossentropy` received `from_logits=True`, but '\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "781/781 [==============================] - 118s 147ms/step - loss: 2.4510 - categorical_accuracy: 0.0990 - val_loss: 2.3257 - val_categorical_accuracy: 0.1001\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:absl:Found untraced functions such as conv2d_layer_call_and_return_conditional_losses, conv2d_layer_call_fn, activation_layer_call_and_return_conditional_losses, activation_layer_call_fn, conv2d_1_layer_call_and_return_conditional_losses while saving (showing 5 of 40). These functions will not be directly callable after loading.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: /Users/hasan/Saved_Models/CIFAR10_VGG/assets\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: /Users/hasan/Saved_Models/CIFAR10_VGG/assets\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 2/5\n",
      "781/781 [==============================] - 117s 150ms/step - loss: 2.3117 - categorical_accuracy: 0.0987 - val_loss: 2.3077 - val_categorical_accuracy: 0.1001\n",
      "Epoch 3/5\n",
      "781/781 [==============================] - 118s 151ms/step - loss: 2.3069 - categorical_accuracy: 0.0995 - val_loss: 2.3073 - val_categorical_accuracy: 0.1001\n",
      "Epoch 4/5\n",
      "781/781 [==============================] - 129s 165ms/step - loss: 2.3065 - categorical_accuracy: 0.0986 - val_loss: 2.3056 - val_categorical_accuracy: 0.0999\n",
      "Epoch 5/5\n",
      "781/781 [==============================] - 116s 148ms/step - loss: 2.3063 - categorical_accuracy: 0.1003 - val_loss: 2.3044 - val_categorical_accuracy: 0.1001\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "The model name variable is used for model serialization\n",
    "'''\n",
    "model_name = \"CIFAR10_VGG\"\n",
    "\n",
    "\n",
    "'''\n",
    "The file path to the Saved_Models diretory in which the best model will be serialized\n",
    "'''\n",
    "saved_model_directory = os.path.join(ROOT_DIR, 'Saved_Models')\n",
    "saved_model_path = os.path.join(saved_model_directory, model_name)\n",
    "\n",
    "\n",
    "'''\n",
    "Create model checkpoint \"callback\" object to save only the best performing model\n",
    "'''\n",
    "checkpoint_cb = tf.keras.callbacks.ModelCheckpoint(filepath=saved_model_path, \n",
    "                                                   monitor='val_categorical_accuracy',\n",
    "                                                   save_best_only=True,\n",
    "                                                   save_weights_only=False)\n",
    "\n",
    "\n",
    "'''\n",
    "Create a learning schedule \"callback\" object \n",
    "'''\n",
    "lschedule_cb = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_categorical_accuracy', factor=0.1,\n",
    "                              patience=10, min_lr=0.000001, mode='auto', verbose=1)\n",
    "\n",
    "\n",
    "'''\n",
    "Train the model\n",
    "'''\n",
    "print(\"\\nTraining started ...\")\n",
    "\n",
    "history = model.fit(train_loader, \n",
    "                    epochs=no_of_epochs,\n",
    "                    verbose=1,\n",
    "                    callbacks=[lschedule_cb, checkpoint_cb],\n",
    "                    validation_data=test_loader,\n",
    "                    steps_per_epoch=train_dataset_size // size_of_mini_batch,\n",
    "                    validation_steps=test_dataset_size // size_of_mini_batch_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Model Evaluation\n",
    "\n",
    "We evaluate the trained model using various techniques.\n",
    "\n",
    "- Evaluate the Model using the History Object\n",
    "- Evaluate the Model using its evaluate() Method on the Batched Test Dataset Object\n",
    "- Evaluate the Model using its predict() Method on the Full Test Set\n",
    "- Evaluation using the Saved Model\n",
    "\n",
    "\n",
    "\n",
    "## Evaluate the Model using the History Object\n",
    "\n",
    "The model's fit method returns a History object, which we use for getting information on the model's performance. \n",
    "\n",
    "The History object has an attribute named \"history\" that contains a record of training loss values and metrics values at successive epochs, as well as validation loss values and validation metrics values (if applicable)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epochs:  5\n",
      "\n",
      "CIFAR10_VGG Train Accuracy: 0.100\n",
      "CIFAR10_VGG Train Loss: 2.306\n",
      "\n",
      "CIFAR10_VGG Validation Accuracy: 0.100\n",
      "CIFAR10_VGG Validation Loss: 2.304\n"
     ]
    }
   ],
   "source": [
    "numOfEpochs = len(history.history['loss'])\n",
    "print(\"Epochs: \", numOfEpochs)\n",
    "\n",
    "train_acc = history.history['categorical_accuracy']\n",
    "val_acc = history.history['val_categorical_accuracy']\n",
    "\n",
    "\n",
    "train_loss = history.history['loss']\n",
    "val_loss = history.history['val_loss']\n",
    "\n",
    "\n",
    "# Read the last value from the list that represents final epoch statistics\n",
    "print(\"\\n{} Train Accuracy: {:.3f}\".format(model_name, train_acc[-1]))\n",
    "print(\"{} Train Loss: {:.3f}\".format(model_name, train_loss[-1]))\n",
    "\n",
    "print(\"\\n{} Validation Accuracy: {:.3f}\".format(model_name, val_acc[-1]))\n",
    "print(\"{} Validation Loss: {:.3f}\".format(model_name, val_loss[-1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate the Model using its evaluate() Method on the Batched Test Dataset Object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Test Accuracy: 0.100\n",
      "Test Loss: 2.304\n"
     ]
    }
   ],
   "source": [
    "test_loss, test_accuracy = model.evaluate(test_loader, verbose=0)\n",
    "print(\"\\nTest Accuracy: {:.3f}\".format(test_accuracy))\n",
    "print(\"Test Loss: {:.3f}\".format(test_loss))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate the Model using its predict() Method on the Full Test Set\n",
    "\n",
    "For evaluating the model using the test set, we need to create: \n",
    "- an array of test images (as Tensors)\n",
    "- an array of integer labels \n",
    "\n",
    "Then, we predict integer lables using the test images and compute model's performance using the true integer labels. Following evaluation metrics are used:\n",
    "- Accuracy\n",
    "- Confusion matrix\n",
    "- Classification report\n",
    "\n",
    "\n",
    "\n",
    "## Create Arrays of Test Images and Labels\n",
    "\n",
    "From the test Dataset object we create an array of tensor images and an array of their labels. The loaded labels are one-hot encoded, which we convert as an array of integer labels. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of Test Images:  10000\n",
      "\n",
      "Test Images Array Shape:  (10000, 32, 32, 3)\n",
      "Test Images Array Data Type:  <dtype: 'float32'>\n",
      "\n",
      "Test Labels Array Shape (one-hot encoded):  (10000, 10)\n",
      "Test Labels Array Data Type (one-hot encoded):  <dtype: 'int32'>\n",
      "\n",
      "Length of the integer labels list:  10000\n",
      "\n",
      "Shape of the integer label array:  (10000,)\n",
      "Data type of the integer label array:  <dtype: 'int64'>\n",
      "\n",
      "Sample one-hot encoded label:  tf.Tensor([0 0 0 0 0 1 0 0 0 0], shape=(10,), dtype=int32)\n",
      "Sample integer label:  tf.Tensor(5, shape=(), dtype=int64)\n"
     ]
    }
   ],
   "source": [
    "# Load the test dataset as image-label (one-hot encoded) pairs\n",
    "ds_test = test_dataset.map(load_labeled_data, num_parallel_calls=tf.data.AUTOTUNE)\n",
    "\n",
    "# Create lists of test images and their labels (one-hot encoded) \n",
    "images_test_list = []\n",
    "labels_test_list = []\n",
    "count = 0\n",
    "for images, labels in ds_test.take(-1):\n",
    "    count += 1\n",
    "    images_test_list.append(images)\n",
    "    labels_test_list.append(labels)\n",
    "\n",
    "print(\"Number of Test Images: \", count)\n",
    "\n",
    "\n",
    "# Convert the lists of images and labels (one-hot encoded) as arrays\n",
    "#images_test = np.asarray(images_test_list)\n",
    "#labels_test_one_hot_encoded = np.asarray(labels_test_list)\n",
    "images_test = tf.convert_to_tensor(images_test_list)\n",
    "labels_test_one_hot_encoded = tf.convert_to_tensor(labels_test_list)\n",
    "\n",
    "print(\"\\nTest Images Array Shape: \", images_test.shape)\n",
    "print(\"Test Images Array Data Type: \", images_test.dtype)\n",
    "\n",
    "\n",
    "print(\"\\nTest Labels Array Shape (one-hot encoded): \", labels_test_one_hot_encoded.shape)\n",
    "print(\"Test Labels Array Data Type (one-hot encoded): \", labels_test_one_hot_encoded.dtype)\n",
    "\n",
    "# Create a list of integer labels by converting one-hot encoded labels \n",
    "labels_test_list = []\n",
    "for i in range(labels_test_one_hot_encoded.shape[0]):\n",
    "    labels_test_list.append(tf.argmax(labels_test_one_hot_encoded[i]))\n",
    "\n",
    "# Number of integer labels \n",
    "print(\"\\nLength of the integer labels list: \", len(labels_test_list))\n",
    "\n",
    "# Convert the integer labels list as a numpy array\n",
    "#labels_test = np.asarray(labels_test_list)\n",
    "labels_test = tf.convert_to_tensor(labels_test_list)\n",
    "print(\"\\nShape of the integer label array: \", labels_test.shape)\n",
    "print(\"Data type of the integer label array: \", labels_test.dtype)\n",
    "\n",
    "print(\"\\nSample one-hot encoded label: \", labels_test_one_hot_encoded[670])\n",
    "print(\"Sample integer label: \", labels_test[670])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Make Predictions using the Test Images Tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Shape of the one-hot encoded predictions array:  (10000, 10)\n",
      "Data type of the one-hot encoded predictions array:  float32\n",
      "\n",
      "Shape of the integer predictions array:  (10000,)\n",
      "Data type of the integer predictions array:  <dtype: 'int64'>\n",
      "\n",
      "Sample one-hot encoded predicted label:  [0.10135484 0.10252091 0.10352198 0.11014185 0.10044903 0.08794268\n",
      " 0.09623654 0.10048236 0.09304459 0.10430516]\n",
      "Sample integer true label:  tf.Tensor(3, shape=(), dtype=int64)\n",
      "\n",
      "Test Accuracy:  0.1\n",
      "\n",
      "Test Confusion Matrix:\n",
      "[[   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]]\n",
      "\n",
      "Classification Report:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.00      0.00      0.00      1000\n",
      "           1       0.00      0.00      0.00      1000\n",
      "           2       0.00      0.00      0.00      1000\n",
      "           3       0.10      1.00      0.18      1000\n",
      "           4       0.00      0.00      0.00      1000\n",
      "           5       0.00      0.00      0.00      1000\n",
      "           6       0.00      0.00      0.00      1000\n",
      "           7       0.00      0.00      0.00      1000\n",
      "           8       0.00      0.00      0.00      1000\n",
      "           9       0.00      0.00      0.00      1000\n",
      "\n",
      "    accuracy                           0.10     10000\n",
      "   macro avg       0.01      0.10      0.02     10000\n",
      "weighted avg       0.01      0.10      0.02     10000\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/hasan/anaconda3/lib/python3.7/site-packages/sklearn/metrics/_classification.py:1272: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, msg_start, len(result))\n"
     ]
    }
   ],
   "source": [
    "# Predict one-hot encoded labels for the test images\n",
    "labels_test_predicted_one_hot_encoded = model.predict(images_test)\n",
    "print(\"\\nShape of the one-hot encoded predictions array: \", labels_test_predicted_one_hot_encoded.shape)\n",
    "print(\"Data type of the one-hot encoded predictions array: \", labels_test_predicted_one_hot_encoded.dtype)\n",
    "\n",
    "# Create an array of integer labels by converting one-hot encoded labels array \n",
    "# This is done by getting the label/index of the highest probability class\n",
    "labels_test_predicted = tf.argmax(labels_test_predicted_one_hot_encoded, axis=1) \n",
    "print(\"\\nShape of the integer predictions array: \", labels_test_predicted.shape)\n",
    "print(\"Data type of the integer predictions array: \", labels_test_predicted.dtype)\n",
    "\n",
    "print(\"\\nSample one-hot encoded predicted label: \", labels_test_predicted_one_hot_encoded[670])\n",
    "print(\"Sample integer true label: \", labels_test_predicted[670])\n",
    "\n",
    "test_accuracy = np.mean(labels_test_predicted == labels_test)\n",
    "print(\"\\nTest Accuracy: \", test_accuracy)\n",
    "\n",
    "print(\"\\nTest Confusion Matrix:\")\n",
    "print(confusion_matrix(labels_test, labels_test_predicted))\n",
    "\n",
    "print(\"\\nClassification Report:\")\n",
    "print(classification_report(labels_test, labels_test_predicted))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluation using the Saved Model\n",
    "\n",
    "We evaluate the saved model using its predict() method on the full test dataset.\n",
    "\n",
    "The saved model can be loaded by using tf.keras.models.load_model() method. If the model contains custom layers, then we need to set the \"custom_objects\" argument. It should be a dictionary mapping names (strings) to custom classes or functions to be considered during deserialization.\n",
    "\n",
    "In this demo, the training model is a custom object. Because it is created using the \"vgg\" function that is based on the custom data augmentation layer and the VGG_Block custom layer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Shape of the one-hot encoded predictions array:  (10000, 10)\n",
      "Data type of the one-hot encoded predictions array:  float32\n",
      "\n",
      "Shape of the integer predictions array:  (10000,)\n",
      "Data type of the integer predictions array:  <dtype: 'int64'>\n",
      "\n",
      "Sample one-hot encoded predicted label:  [0.08406379 0.09997708 0.09609977 0.11676621 0.10804287 0.10144781\n",
      " 0.10118209 0.09660711 0.08838193 0.10743137]\n",
      "Sample integer true label:  tf.Tensor(3, shape=(), dtype=int64)\n",
      "\n",
      "Test Accuracy:  0.1\n",
      "\n",
      "Test Confusion Matrix:\n",
      "[[   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]\n",
      " [   0    0    0 1000    0    0    0    0    0    0]]\n",
      "\n",
      "Classification Report:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.00      0.00      0.00      1000\n",
      "           1       0.00      0.00      0.00      1000\n",
      "           2       0.00      0.00      0.00      1000\n",
      "           3       0.10      1.00      0.18      1000\n",
      "           4       0.00      0.00      0.00      1000\n",
      "           5       0.00      0.00      0.00      1000\n",
      "           6       0.00      0.00      0.00      1000\n",
      "           7       0.00      0.00      0.00      1000\n",
      "           8       0.00      0.00      0.00      1000\n",
      "           9       0.00      0.00      0.00      1000\n",
      "\n",
      "    accuracy                           0.10     10000\n",
      "   macro avg       0.01      0.10      0.02     10000\n",
      "weighted avg       0.01      0.10      0.02     10000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Load the saved model (containing custom objec) using with the custom_object argument\n",
    "saved_model = tf.keras.models.load_model(saved_model_path, custom_objects={\"create_vgg_net\": create_vgg_net})\n",
    "\n",
    "\n",
    "# Predict one-hot encoded labels for the test images\n",
    "labels_test_predicted_one_hot_encoded = saved_model.predict(images_test)\n",
    "print(\"\\nShape of the one-hot encoded predictions array: \", labels_test_predicted_one_hot_encoded.shape)\n",
    "print(\"Data type of the one-hot encoded predictions array: \", labels_test_predicted_one_hot_encoded.dtype)\n",
    "\n",
    "# Create an array of integer labels by converting one-hot encoded labels array \n",
    "# This is done by getting the label/index of the highest probability class\n",
    "labels_test_predicted = tf.argmax(labels_test_predicted_one_hot_encoded, axis=1) \n",
    "print(\"\\nShape of the integer predictions array: \", labels_test_predicted.shape)\n",
    "print(\"Data type of the integer predictions array: \", labels_test_predicted.dtype)\n",
    "\n",
    "print(\"\\nSample one-hot encoded predicted label: \", labels_test_predicted_one_hot_encoded[670])\n",
    "print(\"Sample integer true label: \", labels_test_predicted[670])\n",
    "\n",
    "test_accuracy = np.mean(labels_test_predicted == labels_test)\n",
    "print(\"\\nTest Accuracy: \", test_accuracy)\n",
    "\n",
    "print(\"\\nTest Confusion Matrix:\")\n",
    "print(confusion_matrix(labels_test, labels_test_predicted))\n",
    "\n",
    "print(\"\\nClassification Report:\")\n",
    "print(classification_report(labels_test, labels_test_predicted))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
